﻿using ASmile.Com;
using ASmile.Helpers;
using ASmile.Extend;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ASmile.DBModel;
using ASmile.Com.Da;

namespace ASmile.Web.Authorizes
{
    /// <summary>
    /// 实现本地加密Token
    /// </summary>
    class LoginStateLocal : LoginStateBase
    {
        private static readonly RsaHelper _RSA;

        static LoginStateLocal()
        {
            var rsaPrivateKey = File.ReadAllText(AppContext.BaseDirectory + Config.THIS.RsaTokenKey);
            _RSA = new RsaHelper(rsaPrivateKey);
            Task.Factory.StartNew(InvalidHandle);
        }

        #region 处理无效token

        class InvalidTokenModel
        {
            public DateTime AddTime { set; get; }

            public string Token { set; get; }

            public LoginUser User { set; get; }

            public bool IsAddDb { set; get; }
        }

        static Queue<InvalidTokenModel> InvalidTokens { get; } = new Queue<InvalidTokenModel>();

        static void AddInvalidToken(string token, LoginUser user)
        {
            if (user == null) return;
            InvalidTokenModel m = new InvalidTokenModel
            {
                AddTime = DateTime.Now,
                Token = token,
                User = user,
            };
            InvalidTokens.Enqueue(m);
        }

        static void InvalidHandle()
        {
            LogHelper.LogObj.Info("Local Token 初始化");
            BLLBase BLLObj = new BLLBase();
            //无效Token列表
            var tokenList = BLLObj.DB.Query<Sys_Token>(s => s.ExpiresTime > DateTime.Now && s.TokenMode == 1).ToList();
            LogHelper.LogObj.Info($"无效Token数量为：{tokenList?.Count ?? 0}");
            if (tokenList?.Count > 0)
            {
                foreach (var item in tokenList)
                {
                    if (item.ExpiresTime <= DateTime.Now) continue;
                    MemoryCacheHelper.Set($"K-{item.TokenValue}", "过期", item.ExpiresTime - DateTime.Now);
                }
            }

            while (true)
            {
                try
                {
                    if (InvalidTokens.Count > 0)
                    {
                        if (HandleToken()) //为 False　表示再等待指定延时后再处理
                        {
                            Thread.Sleep(50);
                            continue;
                        }
                    }

                    BLLObj.DB.Delete<Sys_Token>(s => s.ExpiresTime < DateTime.Now);
                }
                catch (Exception ex)
                {
                    LogHelper.LogObj.Error("处理无效Token异常", ex);
                }
                Thread.Sleep(1000 * 10);

            }

            bool HandleToken()
            {
                var m = InvalidTokens.Dequeue();
                if (m.User.ExpiresTime <= DateTime.Now)
                {
                    return true;
                }
                if (!m.IsAddDb)
                {
                    Sys_Token t = new Sys_Token
                    {
                        Id = BLLObj.NewId(),
                        AddTime = DateTime.Now,
                        AddHost = m.User.ClientId,
                        ExpiresTime = m.User.ExpiresTime,
                        TokenMode = 1,
                        TokenValue = m.Token,
                        UserId = m.User.UserId,
                    };
                    BLLObj.DB.Insert(t); //加入到数据库中，当应用重启后还有效
                    m.IsAddDb = true;
                }
                if ((DateTime.Now - m.AddTime).TotalMinutes < 1)
                {
                    //如果添加时间小于1分钟，将要处理的数据加入队列末尾，然后再处理下一个
                    //PS: 刷新Token后，原Token 1分钟之后才无效
                    InvalidTokens.Enqueue(m);
                    return InvalidTokens.Count > 1;  //如果无效 Token 数量为1 表示只有一个时间不够的等待，就需要等待延迟之后再处理了
                }
                MemoryCacheHelper.Set($"K-{m.Token}", "过期", m.User.ExpiresTime - DateTime.Now);
                return true;
            }

            //无效Token处理
        }

        #endregion

        //public override void InitCache()
        //{

        //}

        public override string CreateToken(LoginUser user)
        {
            user.ExpiresTime = DateTime.Now.Add(user.Expires);
            var str = user.ToString();
            var token = _RSA.Encrypt(str);
            return token;
        }

        public override LoginUser Get(string token)
        {
            if (token.IsEmpty()) return null;
            string tokenStr;
            try
            {
                tokenStr = _RSA.Decrypt(token);
            }
            catch (Exception ex)
            {
                LogHelper.LogObj.Error("解析Token字符串异常", ex);
                return null;
            }

            var user = LoginUser.FromString(tokenStr);
            //var user = JsonHelper.Deserialize<LoginUser>(userJson);
            if (user == null) return null;

            if (user.ExpiresTime < DateTime.Now) return null;

            return user;
        }

        public override string RefreshToken(string token, out LoginUser user)
        {
            user = Get(token);
            var newToken = CreateToken(user);
            AddInvalidToken(token, user);
            return newToken;
        }

        public override bool Remove(string token)
        {
            var user = Get(token);
            AddInvalidToken(token, user);
            return true;
        }

        public override string Set(LoginUser user)
        {
            return CreateToken(user);
        }
    }
}